package com.br4mmie.asciistory;

import java.io.*;
import java.util.Arrays;

import com.br4mmie.asciistory.ui.GUIConsole;

/**
 * Class that represents a picture using ASCII symbols
 * treats an adapted String as the image and characters as pixels.
 * @author Niels
 * @author Bram Buurlage
 */
public class CharImage
{
    // instance variables
    protected char[] image; //Character representation of the image
    private char transparent; //Character to be ignored when drawing to other CharImages
    protected int width, height; //Width and height used to determine size and when to end a line
    protected boolean[] mask;

    /** Empty Constructor: Use only for Dummies, Will crash!*/
    public CharImage()
    {	
        width = 0;
        height = 0;
        image = new char[width*height];

        mask = new boolean[height];
    }
    
    /** Standard Constructor, 
	 * Generate a new CharImage of width w and height h and
	 * fill it with '.'
	 */
    public CharImage(int w, int h)
    {
        width = w;
        height = h;
        image = new char[w*h];
        
        mask = new boolean[height];
    }
    
    public CharImage(String imageFile) 
    {
		loadFromFile(imageFile);
	}    
    public void loadFromFile(String filename)
    {
    	BufferedReader in;
    	int w,h;
    	try
    	{
    		in = new BufferedReader(new FileReader(FileGetter.getFilename(filename)));
    		w = Integer.parseInt(in.readLine());
    		h = Integer.parseInt(in.readLine());
            width = w;
            height = h;
            image = new char[w*h];
            
            mask = new boolean[height];
            
            for (int i=0; i<height; i++)
            {
            	writeImage(in.readLine(),0,i);
            }
            
            createMask();
            in.close();            
    	}
    	catch(Exception e)
    	{
    		System.out.println("BLUH in CharImage");
    		e.printStackTrace();
    	}
    }
    public void print(GUIConsole console)
    {
    	console.setText(toString());
    }
    
    public void createMask()
    {
    	for (int y=0; y<height; y++)
    	{
    		for (int x=0; x<width; x++)
    		{
    			if (charAt(x, y) == getTransparent())
    			{
    				mask[y] = true;
    				break;
    			}
    		}
    	}
    }
    
    /** Set Image function,
	 * Attempts to fill image with passed String.
	 * Starts at X,Y = 0,0
	 * If passed String is smaller than image, the remaining image is left untouched.
	 * If passed String is lager than image, remaining String is lost. 
	 */
    public void setImage(String plaatje)
    {
        for(int i = 0; i < height; i++)
        {
            for(int j = 0; j < width; j++)
            {
                if(plaatje.charAt((i*width+j)) == '\n')break;
                else image[((i*width)+j)] = plaatje.charAt((i*width+j));
            }
        }
    }
    
    /**
     * write Image function
     * !! Avoid calling manually, can cause crash !!
     * Meant to be called from other Charimage classes only.
     * Writes passed string to passed position (x,y) in this image.
     */
    public void writeImage(String input, int x, int y)
    {    	
        int posX = x;
        int posY = y;
        for(int i = 0; i < input.length(); i++)
        {
            
            if(input.charAt(i) == '\n')
            {
                posX = x;
                posY++;
            }
            
            else
            {
                putChar(input.charAt(i), posX, posY);
                posX++;
            }
            
        }
    }
    
    /**
     * write Image function
     * !! Avoid calling manually, can cause crash !!
     * Meant to be called from other CharImage classes only.
     * Writes passed image to passed position (x,y) in this image.
     * Supports transparency -> *partially*
     */
    public void writeImage(CharImage input, int x, int y)
    {
    	for(int i = 0; i < input.getHeight(); i++)
        {
    		if (!input.mask[i])
    		{
    			System.arraycopy(
    					input.image, 
    					i*input.width,
    					image, 
    					x+(y+i)*width, 
    					input.width);
    		}
    		else
    		{
    			for (int j=0; j<input.width; j++)
    			{
    				char c = input.charAt(j, i);
    				if (c != input.getTransparent()) putChar(c, x+j, y+i);
    			}
    		}
        }
    }
    
    /**
	 * Basic getter, returns character at position i in image
	 */
    public char charAt(int i)
    {	
    	return image[i];
    }
    
    /**
	 * Basic getter, returns character at position (x,y) in image
	 */
    public char charAt(int x, int y)
    {
    	return image[(y*width)+x];
    }
    
    /**
	 * Basic getter, returns the size of this image
	 */
    public int length()
    {
    	return image.length;
    }
    
    /**
	 * Basic getter, returns character that will be treated as transparent by this image
	 */
    public char getTransparent()
    {
    	return transparent;
    }
    
    /**
     * Basic setter.
     * sets the character that is to be treated as transparent when drawing to other images
     */
    public void setTransparent(char t)
    {
    	transparent = t;
    }
    
    /**
	 * Basic getter, returns width
	 */
    public int getWidth()
    {
        return width;
    }
    
    /**
	 * Simple getter, returns height
	 */
    public int getHeight()
    {
        return height;
    }
    
    /**
	 * Basic toString function
	 * returns the string representation of this image.
	 * each line is separated by newline marker "\n"
	 */
    public String toString()
    {
        String tempstring = "";
        for(int i = 0; i < height; i++)
        {
        	if (i != 0) tempstring += "\n";
            tempstring += new String(image, i*width, width);
        }
        return tempstring;
    }
    
    /**
	 * basic draw function
	 * draws this image to the image of the passed target
	 */
    public void draw(CharImage target)
    {
    	//if(this.width > target.width || this.height > target.height)System.out.println("Error in function Draw(target): target is smaller than source");
    	//else 
    	target.writeImage(this, 0, 0);
    }
    
    /**
	 * basic targeted draw function.
	 * draws this image to the image of the passed target
	 * puts this image (0,0) at the passed (x,y) of the target
	 */
    public void draw(CharImage target, int x, int y)
    {
        //if(this.width > target.width-x || this.height > target.height-y)System.out.println("Error in function Draw(target, x, y): target is smaller than source");
        //else 
        	target.writeImage(this, x, y);
    }
    
    /**
	 * Partial draw function.
	 * Draws a part of this image to a specified spot in the passed targets' image
	 * Creates a new image with width w and height h.
	 * Draws this image to the new image starting at (startX,startY) ending at (startX+w,startY+h)
	 * then draws the new image to the passed target at (targetX,targetY)
	 */
    public void draw(CharImage target, int targetX, int targetY, int startX, int startY, int w, int h)
    {
    	for (int y=0; y<h; y++)
    		System.arraycopy(image, 		startX + (startY+y)*width, 
    						 target.image,  targetX + (targetY+y)*target.width, w);
    }
    
    public int  calcTextHeight(int w, String text)
    {
    	int r = 0;
    	int x = 0;
    	for (char c: text.toCharArray())
    	{
    		if (x >= w) {x=0;r++;}
    	}
    	
    	return r;
    }
    public int  drawText(int x, int y, int w, String text)
    {
    	int r = -1;
    	int l = text.length();
    	char[] textArray = text.toCharArray();
    	
    	while (l > 0)
    	{
    		r++;
    		System.arraycopy(textArray, r*w, image, x+y*width, Math.min(w, l));
    		
    		y++;
    		l -= w;
    	}
		
		return r;
    }
    
    /**
     * Basic charsetter function
     * changes the character at the passed location (x,y) to passed character
     */
    public void putChar(char theChar, int x, int y)
    {
    	image[((y*width)+x)]=theChar;
    }
    
    /**
     * Draw Line function
     * Draws a line consisting of passes character to image
     * Line starts at (x0,y0), line ends at (x1,y1)
     * 
     * Uses the Digital Differential Analyzer algorithm
     */
    public void line(char theChar, int x0, int y0, int x1, int y1)
    {        
        int dx = x1 - x0;
        int dy = y1 - y0;

        putChar(theChar, x0, y0);
        if (Math.abs(dx) > Math.abs(dy)) {          // slope < 1
            float m = (float) dy / (float) dx;      // compute slope
            float b = y0 - m*x0;
            dx = (dx < 0) ? -1 : 1;
            while (x0 != x1) {
                x0 += dx;
                putChar(theChar, x0,Math.round(m*x0 + b));
            }
        } else
        if (dy != 0) {                              // slope >= 1
            float m = (float) dx / (float) dy;      // compute slope
            float b = x0 - m*y0;
            dy = (dy < 0) ? -1 : 1;
            while (y0 != y1) {
                y0 += dy;
                putChar(theChar, Math.round(m*y0 + b), y0);
            }
        }
    }

    /**
     * Draw triangle function
     * draws a triangle with corners at (x0,y0), (x1,y1), (x2,y2)
     * triangle consists of 3 lines of passed char 
     */
    public void triangle(char theChar, int x0, int y0, int x1, int y1, int x2, int y2)
    {
        line(theChar, x0, y0, x1, y1);
        line(theChar, x1, y1, x2, y2);
        line(theChar, x2, y2, x0, y0);
    }
    
    /**
     * Draw rectangle function
     * draws a rectangle between topleft corner(x0,y0) and bottom right(x1,y1)
     * Rectangle consists of 4 lines of passed char 
     */
    public void rect(char theChar, int x0, int y0, int x1, int y1)
    {    	
        line(theChar, x0, y0, x0, y1);
        line(theChar, x0, y1, x1, y1);
        line(theChar, x1, y0, x1, y1);
        line(theChar, x0, y0, x1, y0);
    }
    
    /**
     * Draw rectangle function
     * draws a rectangle between topleft corner(x0,y0) and bottom right(x1,y1)
     * Spaces in rectangle are filled with passed char
     */
    public void fillRect(char theChar, int x0, int y0, int x1, int y1)
    {
        if(x0 > x1)
        {
            int temp = x1;
            x1 = x0;
            x0 = temp;
        }
        if(y0 > y1)
        {
            int temp = y1;
            y1 = y0;
            y0 = temp;
        }
        for(int i = y0; i <= y1; i++)
        {
            for(int j = x0; j <= x1; j++)
            {
                image[(i*width)+j]=theChar;
            }
        }
        
    }
    
    /**
	 * fill Image function
	 * fills image with passed character
	 */
    public void fill(char theChar)
    {
    	Arrays.fill(image, theChar);
    }
}